// internal/gateway/gateway.go
package gateway

import (
	"encoding/json"
	"net/http"

	"mmo/card/internal/protocol"
	"mmo/card/internal/room"

	"github.com/gorilla/websocket"
)

// Envelope 统一的消息协议
type Envelope struct {
	Type     string          `json:"type"`
	RoomID   string          `json:"room_id,omitempty"`
	PlayerID string          `json:"player_id,omitempty"`
	Payload  json.RawMessage `json:"payload,omitempty"`
}

// HandlerFunc 入站消息处理函数
type HandlerFunc func(c *Client, env *protocol.Envelope) error

// Gateway 负责握手与客户端生命周期管理
type Gateway struct {
	upgrader    websocket.Upgrader
	connManager *ConnectionManager
	middleware  []Middleware
	handler     HandlerFunc
	roomMgr     room.RoomManager
}

// NewGateway 构造并初始化默认处理链
func NewGateway(rm room.RoomManager) *Gateway {
	g := &Gateway{
		upgrader: websocket.Upgrader{
			CheckOrigin: func(r *http.Request) bool { return true },
		},
		connManager: NewConnectionManager(),
		roomMgr:     rm,
	}
	g.handler = Chain(g.defaultInboundHandler, g.middleware...)
	return g
}

// HandleWebSocket 升级连接并启动客户端读写泵
func (g *Gateway) HandleWebSocket(w http.ResponseWriter, r *http.Request) {
	conn, err := g.upgrader.Upgrade(w, r, nil)
	if err != nil {
		http.Error(w, "upgrade failed", http.StatusBadRequest)
		return
	}

	client := NewClient(conn, g.connManager, Chain(g.defaultInboundHandler, g.middleware...))
	g.connManager.Register(client)

	go client.ReadPump()
	go client.WritePump()

	_ = client.SendEnvelope(&protocol.Envelope{
		Type:     protocol.TypeHello,
		PlayerID: client.ID(),
	})
}

// Use 注册中间件（按注册顺序包裹）
func (g *Gateway) Use(m Middleware) {
	g.middleware = append(g.middleware, m)
	g.handler = Chain(g.defaultInboundHandler, g.middleware...)
}

// defaultInboundHandler 基础路由：加入/离开/房间广播
func (g *Gateway) defaultInboundHandler(c *Client, env *protocol.Envelope) error {
	switch env.Type {
	case protocol.TypeJoinRoom:
		if env.RoomID == "" {
			return c.SendEnvelope(&protocol.Envelope{Type: protocol.TypeError, Payload: []byte(`"room_id required"`)})
		}
		if _, err := g.roomMgr.Join(env.RoomID, c.ID()); err != nil {
			return c.SendEnvelope(&protocol.Envelope{Type: protocol.TypeError, Payload: []byte(`"` + err.Error() + `"`)})
		}
		// 连接层加入房间，用于广播
		g.connManager.JoinRoom(env.RoomID, c.ID(), c)
		return c.SendEnvelope(&protocol.Envelope{Type: protocol.TypeJoined, RoomID: env.RoomID})

	case protocol.TypeLeaveRoom:
		if c.RoomID() != "" {
			_, _ = g.roomMgr.Leave(c.RoomID(), c.ID())
			g.connManager.LeaveRoom(c.RoomID(), c.ID())
		}
		return c.SendEnvelope(&protocol.Envelope{Type: protocol.TypeLeft, RoomID: c.RoomID()})

	case protocol.TypeRoomBroadcast:
		if c.RoomID() == "" {
			return c.SendEnvelope(&protocol.Envelope{Type: protocol.TypeError, Payload: []byte(`"not in room"`)})
		}
		g.connManager.BroadcastToRoom(c.RoomID(), &protocol.Envelope{
			Type:     protocol.TypeRoomMessage,
			RoomID:   c.RoomID(),
			PlayerID: c.ID(),
			Payload:  env.Payload,
		}, "" /* 可按需排除自己 */)
		return nil

	default:
		return c.SendEnvelope(&protocol.Envelope{Type: protocol.TypeUnknownType, Payload: []byte(`"no handler"`)})
	}
}
